package org.scala.javafx.rich

import javafx.beans.value.WritableValue
import javafx.collections.ObservableList
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Node
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
import java.lang.reflect.Method
import java.lang.reflect.Field

object Enrich {
  def safeNull[T](result: => T): Option[T] = try {
    Some(result)
  } catch {
    case e: NullPointerException => None
  }
  
  implicit def enrichObservableList[T](raw: ObservableList[T]) = new RichObservableList[T](raw)
  implicit def enrichWritableValue[T](raw: WritableValue[T]) = new RichWritableValue[T](raw)
  implicit def enrichNode[T](raw: Node) = new RichNode(raw)
  implicit def enrichJavaList[T](delegate: java.util.Collection[T]) = RichJavaCollection[T](delegate)
  implicit def enrichClass(underlying: Class[_]) = RichClass(underlying)
  implicit def enrichMethod(underlying: Method) = RichMethod(underlying)
  implicit def enrichObject(underlying: Object) = RichObject(underlying)
  implicit def enrichField(underlying: Field) = RichField(underlying)

  type CanSetOnAction = { def setOnAction(x: EventHandler[ActionEvent])}
  implicit def enrichCanSetOnAction(ref: CanSetOnAction) = new RichRef[CanSetOnAction](ref) {
    def onAction(f: ActionEvent => Unit): Unit = new EventHandler[ActionEvent]() {
      def handle(event: ActionEvent): Unit = f(event)
    }
  }

  type CanAddChangeHandler[T] = {
    def addListener(listener: ChangeListener[_ >: T])
    def removeListener(listener: ChangeListener[_ >: T])
  }
  implicit def enrichCanAddChangeHandler[T](ref: CanAddChangeHandler[T]) = {
    new RichRef[CanAddChangeHandler[T]](ref) {
      def addChangeListener[U >: T](f: (ObservableValue[_ <: U], U, U) => Unit): Unit = {
        ref.addListener(new ChangeListener[U] {
          def changed(observableValue: ObservableValue[_ <: U], oldValue: U, newValue: U): Unit = {
            f(observableValue, oldValue, newValue)
          }
        })
      }
      
      def addFirstChangeListener[U >: T](f: (ObservableValue[_ <: U], U, U) => Unit): Unit = {
        val listener = new ChangeListener[U] {
          def changed(observableValue: ObservableValue[_ <: U], oldValue: U, newValue: U): Unit = {
            ref.removeListener(this)
            f(observableValue, oldValue, newValue)
          }
        }
        ref.addListener(listener)
      }
    }
  }
}
